1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 package sun.java2d.pisces;
27
28 import java.util.Arrays;
29 import java.util.Iterator;
30 import static java.lang.Math.ulp;
31 import static java.lang.Math.sqrt;
32
33 import sun.awt.geom.PathConsumer2D;
34
35
36
37
38 final class Stroker implements PathConsumer2D {
39
40 private static final int MOVE_TO = 0;
41 private static final int DRAWING_OP_TO = 1;
42 private static final int CLOSE = 2;
43
44
45
46
47 public static final int JOIN_MITER = 0;
48
49
50
51
52 public static final int JOIN_ROUND = 1;
53
54
55
56
57 public static final int JOIN_BEVEL = 2;
58
59
60
61
62 public static final int CAP_BUTT = 0;
63
64
65
66
67 public static final int CAP_ROUND = 1;
68
69
70
71
72 public static final int CAP_SQUARE = 2;
73
74 private final PathConsumer2D out;
75
76 private final int capStyle;
77 private final int joinStyle;
78
79 private final float lineWidth2;
80
81 private final float[][] offset = new float[3][2];
82 private final float[] miter = new float[2];
83 private final float miterLimitSq;
84
85 private int prev;
86
87
88 private float sx0, sy0, sdx, sdy;
89
90 private float cx0, cy0, cdx, cdy;
91
92
93
94
95
96
97 private float smx, smy, cmx, cmy;
98
99 private final PolyStack reverse = new PolyStack();
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114 public Stroker(PathConsumer2D pc2d,
115 float lineWidth,
116 int capStyle,
117 int joinStyle,
118 float miterLimit)
119 {
120 this.out = pc2d;
121
122 this.lineWidth2 = lineWidth / 2;
123 this.capStyle = capStyle;
124 this.joinStyle = joinStyle;
125
126 float limit = miterLimit * lineWidth2;
127 this.miterLimitSq = limit*limit;
128
129 this.prev = CLOSE;
130 }
131
132 private static void computeOffset(final float lx, final float ly,
133 final float w, final float[] m)
134 {
135 final float len = (float) sqrt(lx*lx + ly*ly);
136 if (len == 0) {
137 m[0] = m[1] = 0;
138 } else {
139 m[0] = (ly * w)/len;
140 m[1] = -(lx * w)/len;
141 }
142 }
143
144
145
146
147
148
149
150
151
152 private static boolean isCW(final float dx1, final float dy1,
153 final float dx2, final float dy2)
154 {
155 return dx1 * dy2 <= dy1 * dx2;
156 }
157
158
159
160
161 private static final float ROUND_JOIN_THRESHOLD = 1000/65536f;
162
163 private void drawRoundJoin(float x, float y,
164 float omx, float omy, float mx, float my,
165 boolean rev,
166 float threshold)
167 {
168 if ((omx == 0 && omy == 0) || (mx == 0 && my == 0)) {
169 return;
170 }
171
172 float domx = omx - mx;
173 float domy = omy - my;
174 float len = domx*domx + domy*domy;
175 if (len < threshold) {
176 return;
177 }
178
179 if (rev) {
180 omx = -omx;
181 omy = -omy;
182 mx = -mx;
183 my = -my;
184 }
185 drawRoundJoin(x, y, omx, omy, mx, my, rev);
186 }
187
188 private void drawRoundJoin(float cx, float cy,
189 float omx, float omy,
190 float mx, float my,
191 boolean rev)
192 {
193
194
195
196 double cosext = omx * mx + omy * my;
197
198
199
200 final int numCurves = cosext >= 0 ? 1 : 2;
201
202 switch (numCurves) {
203 case 1:
204 drawBezApproxForArc(cx, cy, omx, omy, mx, my, rev);
205 break;
206 case 2:
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221 float nx = my - omy, ny = omx - mx;
222 float nlen = (float) sqrt(nx*nx + ny*ny);
223 float scale = lineWidth2/nlen;
224 float mmx = nx * scale, mmy = ny * scale;
225
226
227
228
229 if (rev) {
230 mmx = -mmx;
231 mmy = -mmy;
232 }
233 drawBezApproxForArc(cx, cy, omx, omy, mmx, mmy, rev);
234 drawBezApproxForArc(cx, cy, mmx, mmy, mx, my, rev);
235 break;
236 }
237 }
238
239
240 private void drawBezApproxForArc(final float cx, final float cy,
241 final float omx, final float omy,
242 final float mx, final float my,
243 boolean rev)
244 {
245 float cosext2 = (omx * mx + omy * my) / (2 * lineWidth2 * lineWidth2);
246
247
248
249
250
251 float cv = (float) ((4.0 / 3.0) * sqrt(0.5-cosext2) /
252 (1.0 + sqrt(cosext2+0.5)));
253
254 if (rev) {
255 cv = -cv;
256 }
257 final float x1 = cx + omx;
258 final float y1 = cy + omy;
259 final float x2 = x1 - cv * omy;
260 final float y2 = y1 + cv * omx;
261
262 final float x4 = cx + mx;
263 final float y4 = cy + my;
264 final float x3 = x4 + cv * my;
265 final float y3 = y4 - cv * mx;
266
267 emitCurveTo(x1, y1, x2, y2, x3, y3, x4, y4, rev);
268 }
269
270 private void drawRoundCap(float cx, float cy, float mx, float my) {
271 final float C = 0.5522847498307933f;
272
273
274
275
276
277 emitCurveTo(cx+mx, cy+my,
278 cx+mx-C*my, cy+my+C*mx,
279 cx-my+C*mx, cy+mx+C*my,
280 cx-my, cy+mx,
281 false);
282 emitCurveTo(cx-my, cy+mx,
283 cx-my-C*mx, cy+mx-C*my,
284 cx-mx-C*my, cy-my+C*mx,
285 cx-mx, cy-my,
286 false);
287 }
288
289
290
291
292 private void computeIntersection(final float x0, final float y0,
293 final float x1, final float y1,
294 final float x0p, final float y0p,
295 final float x1p, final float y1p,
296 final float[] m, int off)
297 {
298 float x10 = x1 - x0;
299 float y10 = y1 - y0;
300 float x10p = x1p - x0p;
301 float y10p = y1p - y0p;
302
303 float den = x10*y10p - x10p*y10;
304 float t = x10p*(y0-y0p) - y10p*(x0-x0p);
305 t /= den;
306 m[off++] = x0 + t*x10;
307 m[off] = y0 + t*y10;
308 }
309
310 private void drawMiter(final float pdx, final float pdy,
311 final float x0, final float y0,
312 final float dx, final float dy,
313 float omx, float omy, float mx, float my,
314 boolean rev)
315 {
316 if ((mx == omx && my == omy) ||
317 (pdx == 0 && pdy == 0) ||
318 (dx == 0 && dy == 0))
319 {
320 return;
321 }
322
323 if (rev) {
324 omx = -omx;
325 omy = -omy;
326 mx = -mx;
327 my = -my;
328 }
329
330 computeIntersection((x0 - pdx) + omx, (y0 - pdy) + omy, x0 + omx, y0 + omy,
331 (dx + x0) + mx, (dy + y0) + my, x0 + mx, y0 + my,
332 miter, 0);
333
334 float lenSq = (miter[0]-x0)*(miter[0]-x0) + (miter[1]-y0)*(miter[1]-y0);
335
336
337
338
339
340
341 if (lenSq < miterLimitSq) {
342 emitLineTo(miter[0], miter[1], rev);
343 }
344 }
345
346 public void moveTo(float x0, float y0) {
347 if (prev == DRAWING_OP_TO) {
348 finish();
349 }
350 this.sx0 = this.cx0 = x0;
351 this.sy0 = this.cy0 = y0;
352 this.cdx = this.sdx = 1;
353 this.cdy = this.sdy = 0;
354 this.prev = MOVE_TO;
355 }
356
357 public void lineTo(float x1, float y1) {
358 float dx = x1 - cx0;
359 float dy = y1 - cy0;
360 if (dx == 0f && dy == 0f) {
361 dx = 1;
362 }
363 computeOffset(dx, dy, lineWidth2, offset[0]);
364 float mx = offset[0][0];
365 float my = offset[0][1];
366
367 drawJoin(cdx, cdy, cx0, cy0, dx, dy, cmx, cmy, mx, my);
368
369 emitLineTo(cx0 + mx, cy0 + my);
370 emitLineTo(x1 + mx, y1 + my);
371
372 emitLineTo(cx0 - mx, cy0 - my, true);
373 emitLineTo(x1 - mx, y1 - my, true);
374
375 this.cmx = mx;
376 this.cmy = my;
377 this.cdx = dx;
378 this.cdy = dy;
379 this.cx0 = x1;
380 this.cy0 = y1;
381 this.prev = DRAWING_OP_TO;
382 }
383
384 public void closePath() {
385 if (prev != DRAWING_OP_TO) {
386 if (prev == CLOSE) {
387 return;
388 }
389 emitMoveTo(cx0, cy0 - lineWidth2);
390 this.cmx = this.smx = 0;
391 this.cmy = this.smy = -lineWidth2;
392 this.cdx = this.sdx = 1;
393 this.cdy = this.sdy = 0;
394 finish();
395 return;
396 }
397
398 if (cx0 != sx0 || cy0 != sy0) {
399 lineTo(sx0, sy0);
400 }
401
402 drawJoin(cdx, cdy, cx0, cy0, sdx, sdy, cmx, cmy, smx, smy);
403
404 emitLineTo(sx0 + smx, sy0 + smy);
405
406 emitMoveTo(sx0 - smx, sy0 - smy);
407 emitReverse();
408
409 this.prev = CLOSE;
410 emitClose();
411 }
412
413 private void emitReverse() {
414 while(!reverse.isEmpty()) {
415 reverse.pop(out);
416 }
417 }
418
419 public void pathDone() {
420 if (prev == DRAWING_OP_TO) {
421 finish();
422 }
423
424 out.pathDone();
425
426
427 this.prev = CLOSE;
428 }
429
430 private void finish() {
431 if (capStyle == CAP_ROUND) {
432 drawRoundCap(cx0, cy0, cmx, cmy);
433 } else if (capStyle == CAP_SQUARE) {
434 emitLineTo(cx0 - cmy + cmx, cy0 + cmx + cmy);
435 emitLineTo(cx0 - cmy - cmx, cy0 + cmx - cmy);
436 }
437
438 emitReverse();
439
440 if (capStyle == CAP_ROUND) {
441 drawRoundCap(sx0, sy0, -smx, -smy);
442 } else if (capStyle == CAP_SQUARE) {
443 emitLineTo(sx0 + smy - smx, sy0 - smx - smy);
444 emitLineTo(sx0 + smy + smx, sy0 - smx + smy);
445 }
446
447 emitClose();
448 }
449
450 private void emitMoveTo(final float x0, final float y0) {
451 out.moveTo(x0, y0);
452 }
453
454 private void emitLineTo(final float x1, final float y1) {
455 out.lineTo(x1, y1);
456 }
457
458 private void emitLineTo(final float x1, final float y1,
459 final boolean rev)
460 {
461 if (rev) {
462 reverse.pushLine(x1, y1);
463 } else {
464 emitLineTo(x1, y1);
465 }
466 }
467
468 private void emitQuadTo(final float x0, final float y0,
469 final float x1, final float y1,
470 final float x2, final float y2, final boolean rev)
471 {
472 if (rev) {
473 reverse.pushQuad(x0, y0, x1, y1);
474 } else {
475 out.quadTo(x1, y1, x2, y2);
476 }
477 }
478
479 private void emitCurveTo(final float x0, final float y0,
480 final float x1, final float y1,
481 final float x2, final float y2,
482 final float x3, final float y3, final boolean rev)
483 {
484 if (rev) {
485 reverse.pushCubic(x0, y0, x1, y1, x2, y2);
486 } else {
487 out.curveTo(x1, y1, x2, y2, x3, y3);
488 }
489 }
490
491 private void emitClose() {
492 out.closePath();
493 }
494
495 private void drawJoin(float pdx, float pdy,
496 float x0, float y0,
497 float dx, float dy,
498 float omx, float omy,
499 float mx, float my)
500 {
501 if (prev != DRAWING_OP_TO) {
502 emitMoveTo(x0 + mx, y0 + my);
503 this.sdx = dx;
504 this.sdy = dy;
505 this.smx = mx;
506 this.smy = my;
507 } else {
508 boolean cw = isCW(pdx, pdy, dx, dy);
509 if (joinStyle == JOIN_MITER) {
510 drawMiter(pdx, pdy, x0, y0, dx, dy, omx, omy, mx, my, cw);
511 } else if (joinStyle == JOIN_ROUND) {
512 drawRoundJoin(x0, y0,
513 omx, omy,
514 mx, my, cw,
515 ROUND_JOIN_THRESHOLD);
516 }
517 emitLineTo(x0, y0, !cw);
518 }
519 prev = DRAWING_OP_TO;
520 }
521
522 private static boolean within(final float x1, final float y1,
523 final float x2, final float y2,
524 final float ERR)
525 {
526 assert ERR > 0 : "";
527
528
529 return (Helpers.within(x1, x2, ERR) &&
530 Helpers.within(y1, y2, ERR));
531 }
532
533 private void getLineOffsets(float x1, float y1,
534 float x2, float y2,
535 float[] left, float[] right) {
536 computeOffset(x2 - x1, y2 - y1, lineWidth2, offset[0]);
537 left[0] = x1 + offset[0][0];
538 left[1] = y1 + offset[0][1];
539 left[2] = x2 + offset[0][0];
540 left[3] = y2 + offset[0][1];
541 right[0] = x1 - offset[0][0];
542 right[1] = y1 - offset[0][1];
543 right[2] = x2 - offset[0][0];
544 right[3] = y2 - offset[0][1];
545 }
546
547 private int computeOffsetCubic(float[] pts, final int off,
548 float[] leftOff, float[] rightOff)
549 {
550
551
552
553
554
555
556
557 final float x1 = pts[off + 0], y1 = pts[off + 1];
558 final float x2 = pts[off + 2], y2 = pts[off + 3];
559 final float x3 = pts[off + 4], y3 = pts[off + 5];
560 final float x4 = pts[off + 6], y4 = pts[off + 7];
561
562 float dx4 = x4 - x3;
563 float dy4 = y4 - y3;
564 float dx1 = x2 - x1;
565 float dy1 = y2 - y1;
566
567
568
569 final boolean p1eqp2 = within(x1,y1,x2,y2, 6 * ulp(y2));
570 final boolean p3eqp4 = within(x3,y3,x4,y4, 6 * ulp(y4));
571 if (p1eqp2 && p3eqp4) {
572 getLineOffsets(x1, y1, x4, y4, leftOff, rightOff);
573 return 4;
574 } else if (p1eqp2) {
575 dx1 = x3 - x1;
576 dy1 = y3 - y1;
577 } else if (p3eqp4) {
578 dx4 = x4 - x2;
579 dy4 = y4 - y2;
580 }
581
582
583 float dotsq = (dx1 * dx4 + dy1 * dy4);
584 dotsq = dotsq * dotsq;
585 float l1sq = dx1 * dx1 + dy1 * dy1, l4sq = dx4 * dx4 + dy4 * dy4;
586 if (Helpers.within(dotsq, l1sq * l4sq, 4 * ulp(dotsq))) {
587 getLineOffsets(x1, y1, x4, y4, leftOff, rightOff);
588 return 4;
589 }
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638 float x = 0.125f * (x1 + 3 * (x2 + x3) + x4);
639 float y = 0.125f * (y1 + 3 * (y2 + y3) + y4);
640
641
642 float dxm = x3 + x4 - x1 - x2, dym = y3 + y4 - y1 - y2;
643
644
645
646
647 computeOffset(dx1, dy1, lineWidth2, offset[0]);
648 computeOffset(dxm, dym, lineWidth2, offset[1]);
649 computeOffset(dx4, dy4, lineWidth2, offset[2]);
650 float x1p = x1 + offset[0][0];
651 float y1p = y1 + offset[0][1];
652 float xi = x + offset[1][0];
653 float yi = y + offset[1][1];
654 float x4p = x4 + offset[2][0];
655 float y4p = y4 + offset[2][1];
656
657 float invdet43 = 4f / (3f * (dx1 * dy4 - dy1 * dx4));
658
659 float two_pi_m_p1_m_p4x = 2*xi - x1p - x4p;
660 float two_pi_m_p1_m_p4y = 2*yi - y1p - y4p;
661 float c1 = invdet43 * (dy4 * two_pi_m_p1_m_p4x - dx4 * two_pi_m_p1_m_p4y);
662 float c2 = invdet43 * (dx1 * two_pi_m_p1_m_p4y - dy1 * two_pi_m_p1_m_p4x);
663
664 float x2p, y2p, x3p, y3p;
665 x2p = x1p + c1*dx1;
666 y2p = y1p + c1*dy1;
667 x3p = x4p + c2*dx4;
668 y3p = y4p + c2*dy4;
669
670 leftOff[0] = x1p; leftOff[1] = y1p;
671 leftOff[2] = x2p; leftOff[3] = y2p;
672 leftOff[4] = x3p; leftOff[5] = y3p;
673 leftOff[6] = x4p; leftOff[7] = y4p;
674
675 x1p = x1 - offset[0][0]; y1p = y1 - offset[0][1];
676 xi = xi - 2 * offset[1][0]; yi = yi - 2 * offset[1][1];
677 x4p = x4 - offset[2][0]; y4p = y4 - offset[2][1];
678
679 two_pi_m_p1_m_p4x = 2*xi - x1p - x4p;
680 two_pi_m_p1_m_p4y = 2*yi - y1p - y4p;
681 c1 = invdet43 * (dy4 * two_pi_m_p1_m_p4x - dx4 * two_pi_m_p1_m_p4y);
682 c2 = invdet43 * (dx1 * two_pi_m_p1_m_p4y - dy1 * two_pi_m_p1_m_p4x);
683
684 x2p = x1p + c1*dx1;
685 y2p = y1p + c1*dy1;
686 x3p = x4p + c2*dx4;
687 y3p = y4p + c2*dy4;
688
689 rightOff[0] = x1p; rightOff[1] = y1p;
690 rightOff[2] = x2p; rightOff[3] = y2p;
691 rightOff[4] = x3p; rightOff[5] = y3p;
692 rightOff[6] = x4p; rightOff[7] = y4p;
693 return 8;
694 }
695
696
697 private int computeOffsetQuad(float[] pts, final int off,
698 float[] leftOff, float[] rightOff)
699 {
700 final float x1 = pts[off + 0], y1 = pts[off + 1];
701 final float x2 = pts[off + 2], y2 = pts[off + 3];
702 final float x3 = pts[off + 4], y3 = pts[off + 5];
703
704 final float dx3 = x3 - x2;
705 final float dy3 = y3 - y2;
706 final float dx1 = x2 - x1;
707 final float dy1 = y2 - y1;
708
709
710 computeOffset(dx1, dy1, lineWidth2, offset[0]);
711 computeOffset(dx3, dy3, lineWidth2, offset[1]);
712
713 leftOff[0] = x1 + offset[0][0]; leftOff[1] = y1 + offset[0][1];
714 leftOff[4] = x3 + offset[1][0]; leftOff[5] = y3 + offset[1][1];
715 rightOff[0] = x1 - offset[0][0]; rightOff[1] = y1 - offset[0][1];
716 rightOff[4] = x3 - offset[1][0]; rightOff[5] = y3 - offset[1][1];
717
718 float x1p = leftOff[0];
719 float y1p = leftOff[1];
720 float x3p = leftOff[4];
721 float y3p = leftOff[5];
722
723
724
725
726
727
728
729
730
731
732 computeIntersection(x1p, y1p, x1p+dx1, y1p+dy1, x3p, y3p, x3p-dx3, y3p-dy3, leftOff, 2);
733 float cx = leftOff[2];
734 float cy = leftOff[3];
735
736 if (!(isFinite(cx) && isFinite(cy))) {
737
738 x1p = rightOff[0];
739 y1p = rightOff[1];
740 x3p = rightOff[4];
741 y3p = rightOff[5];
742 computeIntersection(x1p, y1p, x1p+dx1, y1p+dy1, x3p, y3p, x3p-dx3, y3p-dy3, rightOff, 2);
743 cx = rightOff[2];
744 cy = rightOff[3];
745 if (!(isFinite(cx) && isFinite(cy))) {
746
747 getLineOffsets(x1, y1, x3, y3, leftOff, rightOff);
748 return 4;
749 }
750
751 leftOff[2] = 2*x2 - cx;
752 leftOff[3] = 2*y2 - cy;
753 return 6;
754 }
755
756
757
758 rightOff[2] = 2*x2 - cx;
759 rightOff[3] = 2*y2 - cy;
760 return 6;
761 }
762
763 private static boolean isFinite(float x) {
764 return (Float.NEGATIVE_INFINITY < x && x < Float.POSITIVE_INFINITY);
765 }
766
767
768
769
770 private float[] middle = new float[2*8];
771 private float[] lp = new float[8];
772 private float[] rp = new float[8];
773 private static final int MAX_N_CURVES = 11;
774 private float[] subdivTs = new float[MAX_N_CURVES - 1];
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885 private static Curve c = new Curve();
886 private static int findSubdivPoints(float[] pts, float[] ts, final int type, final float w)
887 {
888 final float x12 = pts[2] - pts[0];
889 final float y12 = pts[3] - pts[1];
890
891
892 if (y12 != 0f && x12 != 0f) {
893
894
895
896 final float hypot = (float) sqrt(x12 * x12 + y12 * y12);
897 final float cos = x12 / hypot;
898 final float sin = y12 / hypot;
899 final float x1 = cos * pts[0] + sin * pts[1];
900 final float y1 = cos * pts[1] - sin * pts[0];
901 final float x2 = cos * pts[2] + sin * pts[3];
902 final float y2 = cos * pts[3] - sin * pts[2];
903 final float x3 = cos * pts[4] + sin * pts[5];
904 final float y3 = cos * pts[5] - sin * pts[4];
905 switch(type) {
906 case 8:
907 final float x4 = cos * pts[6] + sin * pts[7];
908 final float y4 = cos * pts[7] - sin * pts[6];
909 c.set(x1, y1, x2, y2, x3, y3, x4, y4);
910 break;
911 case 6:
912 c.set(x1, y1, x2, y2, x3, y3);
913 break;
914 }
915 } else {
916 c.set(pts, type);
917 }
918
919 int ret = 0;
920
921
922 ret += c.dxRoots(ts, ret);
923 ret += c.dyRoots(ts, ret);
924
925 if (type == 8) {
926
927 ret += c.infPoints(ts, ret);
928 }
929
930
931
932 ret += c.rootsOfROCMinusW(ts, ret, w, 0.0001f);
933
934 ret = Helpers.filterOutNotInAB(ts, 0, ret, 0.0001f, 0.9999f);
935 Helpers.isort(ts, 0, ret);
936 return ret;
937 }
938
939 @Override public void curveTo(float x1, float y1,
940 float x2, float y2,
941 float x3, float y3)
942 {
943 middle[0] = cx0; middle[1] = cy0;
944 middle[2] = x1; middle[3] = y1;
945 middle[4] = x2; middle[5] = y2;
946 middle[6] = x3; middle[7] = y3;
947
948
949
950
951
952 final float xf = middle[6], yf = middle[7];
953 float dxs = middle[2] - middle[0];
954 float dys = middle[3] - middle[1];
955 float dxf = middle[6] - middle[4];
956 float dyf = middle[7] - middle[5];
957
958 boolean p1eqp2 = (dxs == 0f && dys == 0f);
959 boolean p3eqp4 = (dxf == 0f && dyf == 0f);
960 if (p1eqp2) {
961 dxs = middle[4] - middle[0];
962 dys = middle[5] - middle[1];
963 if (dxs == 0f && dys == 0f) {
964 dxs = middle[6] - middle[0];
965 dys = middle[7] - middle[1];
966 }
967 }
968 if (p3eqp4) {
969 dxf = middle[6] - middle[2];
970 dyf = middle[7] - middle[3];
971 if (dxf == 0f && dyf == 0f) {
972 dxf = middle[6] - middle[0];
973 dyf = middle[7] - middle[1];
974 }
975 }
976 if (dxs == 0f && dys == 0f) {
977
978 lineTo(middle[0], middle[1]);
979 return;
980 }
981
982
983
984 if (Math.abs(dxs) < 0.1f && Math.abs(dys) < 0.1f) {
985 float len = (float) sqrt(dxs*dxs + dys*dys);
986 dxs /= len;
987 dys /= len;
988 }
989 if (Math.abs(dxf) < 0.1f && Math.abs(dyf) < 0.1f) {
990 float len = (float) sqrt(dxf*dxf + dyf*dyf);
991 dxf /= len;
992 dyf /= len;
993 }
994
995 computeOffset(dxs, dys, lineWidth2, offset[0]);
996 final float mx = offset[0][0];
997 final float my = offset[0][1];
998 drawJoin(cdx, cdy, cx0, cy0, dxs, dys, cmx, cmy, mx, my);
999
1000 int nSplits = findSubdivPoints(middle, subdivTs, 8, lineWidth2);
1001
1002 int kind = 0;
1003 Iterator<Integer> it = Curve.breakPtsAtTs(middle, 8, subdivTs, nSplits);
1004 while(it.hasNext()) {
1005 int curCurveOff = it.next();
1006
1007 kind = computeOffsetCubic(middle, curCurveOff, lp, rp);
1008 emitLineTo(lp[0], lp[1]);
1009 switch(kind) {
1010 case 8:
1011 emitCurveTo(lp[0], lp[1], lp[2], lp[3], lp[4], lp[5], lp[6], lp[7], false);
1012 emitCurveTo(rp[0], rp[1], rp[2], rp[3], rp[4], rp[5], rp[6], rp[7], true);
1013 break;
1014 case 4:
1015 emitLineTo(lp[2], lp[3]);
1016 emitLineTo(rp[0], rp[1], true);
1017 break;
1018 }
1019 emitLineTo(rp[kind - 2], rp[kind - 1], true);
1020 }
1021
1022 this.cmx = (lp[kind - 2] - rp[kind - 2]) / 2;
1023 this.cmy = (lp[kind - 1] - rp[kind - 1]) / 2;
1024 this.cdx = dxf;
1025 this.cdy = dyf;
1026 this.cx0 = xf;
1027 this.cy0 = yf;
1028 this.prev = DRAWING_OP_TO;
1029 }
1030
1031 @Override public void quadTo(float x1, float y1, float x2, float y2) {
1032 middle[0] = cx0; middle[1] = cy0;
1033 middle[2] = x1; middle[3] = y1;
1034 middle[4] = x2; middle[5] = y2;
1035
1036
1037
1038
1039
1040 final float xf = middle[4], yf = middle[5];
1041 float dxs = middle[2] - middle[0];
1042 float dys = middle[3] - middle[1];
1043 float dxf = middle[4] - middle[2];
1044 float dyf = middle[5] - middle[3];
1045 if ((dxs == 0f && dys == 0f) || (dxf == 0f && dyf == 0f)) {
1046 dxs = dxf = middle[4] - middle[0];
1047 dys = dyf = middle[5] - middle[1];
1048 }
1049 if (dxs == 0f && dys == 0f) {
1050
1051 lineTo(middle[0], middle[1]);
1052 return;
1053 }
1054
1055
1056 if (Math.abs(dxs) < 0.1f && Math.abs(dys) < 0.1f) {
1057 float len = (float) sqrt(dxs*dxs + dys*dys);
1058 dxs /= len;
1059 dys /= len;
1060 }
1061 if (Math.abs(dxf) < 0.1f && Math.abs(dyf) < 0.1f) {
1062 float len = (float) sqrt(dxf*dxf + dyf*dyf);
1063 dxf /= len;
1064 dyf /= len;
1065 }
1066
1067 computeOffset(dxs, dys, lineWidth2, offset[0]);
1068 final float mx = offset[0][0];
1069 final float my = offset[0][1];
1070 drawJoin(cdx, cdy, cx0, cy0, dxs, dys, cmx, cmy, mx, my);
1071
1072 int nSplits = findSubdivPoints(middle, subdivTs, 6, lineWidth2);
1073
1074 int kind = 0;
1075 Iterator<Integer> it = Curve.breakPtsAtTs(middle, 6, subdivTs, nSplits);
1076 while(it.hasNext()) {
1077 int curCurveOff = it.next();
1078
1079 kind = computeOffsetQuad(middle, curCurveOff, lp, rp);
1080 emitLineTo(lp[0], lp[1]);
1081 switch(kind) {
1082 case 6:
1083 emitQuadTo(lp[0], lp[1], lp[2], lp[3], lp[4], lp[5], false);
1084 emitQuadTo(rp[0], rp[1], rp[2], rp[3], rp[4], rp[5], true);
1085 break;
1086 case 4:
1087 emitLineTo(lp[2], lp[3]);
1088 emitLineTo(rp[0], rp[1], true);
1089 break;
1090 }
1091 emitLineTo(rp[kind - 2], rp[kind - 1], true);
1092 }
1093
1094 this.cmx = (lp[kind - 2] - rp[kind - 2]) / 2;
1095 this.cmy = (lp[kind - 1] - rp[kind - 1]) / 2;
1096 this.cdx = dxf;
1097 this.cdy = dyf;
1098 this.cx0 = xf;
1099 this.cy0 = yf;
1100 this.prev = DRAWING_OP_TO;
1101 }
1102
1103 @Override public long getNativeConsumer() {
1104 throw new InternalError("Stroker doesn't use a native consumer");
1105 }
1106
1107
1108
1109 private static final class PolyStack {
1110 float[] curves;
1111 int end;
1112 int[] curveTypes;
1113 int numCurves;
1114
1115 private static final int INIT_SIZE = 50;
1116
1117 PolyStack() {
1118 curves = new float[8 * INIT_SIZE];
1119 curveTypes = new int[INIT_SIZE];
1120 end = 0;
1121 numCurves = 0;
1122 }
1123
1124 public boolean isEmpty() {
1125 return numCurves == 0;
1126 }
1127
1128 private void ensureSpace(int n) {
1129 if (end + n >= curves.length) {
1130 int newSize = (end + n) * 2;
1131 curves = Arrays.copyOf(curves, newSize);
1132 }
1133 if (numCurves >= curveTypes.length) {
1134 int newSize = numCurves * 2;
1135 curveTypes = Arrays.copyOf(curveTypes, newSize);
1136 }
1137 }
1138
1139 public void pushCubic(float x0, float y0,
1140 float x1, float y1,
1141 float x2, float y2)
1142 {
1143 ensureSpace(6);
1144 curveTypes[numCurves++] = 8;
1145
1146
1147
1148 curves[end++] = x2; curves[end++] = y2;
1149 curves[end++] = x1; curves[end++] = y1;
1150 curves[end++] = x0; curves[end++] = y0;
1151 }
1152
1153 public void pushQuad(float x0, float y0,
1154 float x1, float y1)
1155 {
1156 ensureSpace(4);
1157 curveTypes[numCurves++] = 6;
1158
1159 curves[end++] = x1; curves[end++] = y1;
1160 curves[end++] = x0; curves[end++] = y0;
1161 }
1162
1163 public void pushLine(float x, float y) {
1164 ensureSpace(2);
1165 curveTypes[numCurves++] = 4;
1166
1167 curves[end++] = x; curves[end++] = y;
1168 }
1169
1170 @SuppressWarnings("unused")
1171 public int pop(float[] pts) {
1172 int ret = curveTypes[numCurves - 1];
1173 numCurves--;
1174 end -= (ret - 2);
1175 System.arraycopy(curves, end, pts, 0, ret - 2);
1176 return ret;
1177 }
1178
1179 public void pop(PathConsumer2D io) {
1180 numCurves--;
1181 int type = curveTypes[numCurves];
1182 end -= (type - 2);
1183 switch(type) {
1184 case 8:
1185 io.curveTo(curves[end+0], curves[end+1],
1186 curves[end+2], curves[end+3],
1187 curves[end+4], curves[end+5]);
1188 break;
1189 case 6:
1190 io.quadTo(curves[end+0], curves[end+1],
1191 curves[end+2], curves[end+3]);
1192 break;
1193 case 4:
1194 io.lineTo(curves[end], curves[end+1]);
1195 }
1196 }
1197
1198 @Override
1199 public String toString() {
1200 String ret = "";
1201 int nc = numCurves;
1202 int end = this.end;
1203 while (nc > 0) {
1204 nc--;
1205 int type = curveTypes[numCurves];
1206 end -= (type - 2);
1207 switch(type) {
1208 case 8:
1209 ret += "cubic: ";
1210 break;
1211 case 6:
1212 ret += "quad: ";
1213 break;
1214 case 4:
1215 ret += "line: ";
1216 break;
1217 }
1218 ret += Arrays.toString(Arrays.copyOfRange(curves, end, end+type-2)) + "\n";
1219 }
1220 return ret;
1221 }
1222 }
1223 }